﻿/* ===================================尊重原著======================================
 * 创 建 者：mao xiao jun 
 * 创建日期：2021-12-12
 * CLR Version : any
 * ===================================尊重原著=====================================*/
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;

namespace MTP
{
    class MtpService : MtpDispatch, IMtpService
    {
        private readonly int readTimeout = 1000 * 60 * 5;

        private readonly TcpListener tcpListener;

        private bool IsRunning;


        public MtpService(int port, string cipher) : this(port, IPAddress.Any, cipher)
        {

        }

        public MtpService(int port, IPAddress localaddr, string cipher) : base(cipher)
        {
            tcpListener = new TcpListener(localaddr, port);
        }

        public void Start()
        {
            IsRunning = true;
            tcpListener.Start();
            tcpListener.BeginAcceptTcpClient(AcceptCallback, this);
        }

        public void StartSync()
        {
            IsRunning = true;
            tcpListener.Start();
            while (IsRunning)
            {
                var client = tcpListener.AcceptTcpClient();
                Task.Run(() => OnClient(client));
            }
        }

        public void Stop()
        {
            IsRunning = false;
            tcpListener.Stop();
        }

        private void AcceptCallback(IAsyncResult ar)
        {
            if (!IsRunning) return;
            var client = tcpListener.EndAcceptTcpClient(ar);
            tcpListener.BeginAcceptTcpClient(AcceptCallback, this);
            Task.Run(() => OnClient(client));
        }


        private async Task OnClient(TcpClient client)
        {
            NetworkStream stream = client.GetStream();
            if (!await VerifyHandshake(stream))
            {
                client.Close();
                return;
            }
            await SendHandshake(stream);
            while (IsRunning)
            {
                try
                {
                    await ReadNext(stream, new CancellationTokenSource(readTimeout).Token);
                }
                catch
                {
                    client.Close();
                    client.Dispose();
                    break;
                }
            }
        }

        protected override async Task ReadData(Guid guid, Stream stream, string header, NetworkStream networkStream)
        {
            try
            {
                var result = OnReceiveEvent?.Invoke(stream, header);
                if (result == null)
                {
                    await SendVoid(guid, networkStream);
                }
                else
                {
                    await SendData(guid, networkStream, result, null);
                }
            }
            catch (Exception e)
            {
                if (OnErrorEvent != null)
                {
                    try
                    {
                        OnErrorEvent.Invoke(guid, e);
                    }
                    catch (Exception ex)
                    {
                        await SendError(guid, networkStream, ex);
                    }
                }
                else
                {
                    await SendError(guid, networkStream, e);
                }
            }
        }

        public event Func<Stream, string, Stream> OnReceiveEvent;

        public event Action<Guid, Exception> OnErrorEvent;

        public IMtpService OnReceive(Func<Stream, string, Stream> onReceiveEvent)
        {
            OnReceiveEvent += onReceiveEvent;
            return this;
        }

        public IMtpService OnError(Action<Guid, Exception> onErrorEvent)
        {
            OnErrorEvent += onErrorEvent;
            return this;
        }
    }

}
